import logging
import re
import urlparse 
import time
import os

from datetime import datetime, timedelta

import wsgiref.handlers

from google.appengine.ext import db
from google.appengine.api import memcache
from google.appengine.api import urlfetch
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template

from BeautifulSoup import BeautifulSoup
from BeautifulSoup import BeautifulStoneSoup


_RADAR_BASE_URL = 'http://wetterstationen.meteomedia.de/messnetz/radar/radar.html'
_MAP_TEMPLATE = os.path.join(os.path.dirname(__file__), 'wetter', 'map.tpl.html')


class RadarUrl(db.Model):
  last_modified = db.DateTimeProperty(required=True)
  expires = db.DateTimeProperty(required=True)
  max_age = db.FloatProperty(required=True)
  url = db.StringProperty(required=False)

class RadarTimestamp(db.Model):
  time = db.StringProperty(required=True)
  delta = db.FloatProperty(required=True)
  last_modified = db.DateTimeProperty(required=True)


class RadarHandler(webapp.RequestHandler):

  def getRadarOverlaysJS(self, radarUrl):
    key = 'RadarOverlaysJS-%s' % radarUrl.url
    overlays = memcache.get(key)
    if not overlays:
      overlays = self.getRadarOverlays(radarUrl)
      if overlays:
        overlays = str(overlays)
        memcache.set(key, overlays, radarUrl.max_age)
    return overlays;
    
  def getRadarOverlays(self, radarUrl):
    if not radarUrl.url:
      return

    key = 'RadarOverlays-%s' % radarUrl.url
    overlays = memcache.get(key)

    if overlays:
      return overlays;

    response = urlfetch.fetch(radarUrl.url)

    if response.status_code != 200:
      logging.error('cannot get radar image: %i', response.status_code)
      return

    doc = BeautifulStoneSoup(response.content, convertEntities=BeautifulSoup.ALL_ENTITIES)
    groundoverlay = doc.findAll('groundoverlay')

    if not groundoverlay:
      logging.error('no GroundOverlay found')
      return

    overlays = []
    
    for overlay in groundoverlay:
      image = {}
      image['name'] = str(overlay.nameTag.string)
      image['href'] = str(overlay.icon.href.string)
      image['north'] = str(overlay.latlonbox.north.string)
      image['south'] = str(overlay.latlonbox.south.string)
      image['east'] = str(overlay.latlonbox.east.string)
      image['west'] = str(overlay.latlonbox.west.string)
      overlays.append(image)

    memcache.Client().set(key, overlays, radarUrl.max_age)

    return overlays


  def getRadarUrl(self):
    now = datetime.utcnow()

    radarUrl = memcache.get('RadarUrl')
    if radarUrl and now < radarUrl.expires:
      logging.info('url from memcache')
      logging.info(' now: %s', _fdatetime(now))
      logging.info(' expires: %s', _fdatetime(radarUrl.expires))
      return radarUrl

    radarUrl = self.queryRadarUrl()
    if not radarUrl:
      radarUrl = RadarUrl(key_name='url', last_modified=now, 
                          expires=now, max_age=15.0*60, url='')

    if now < radarUrl.expires:
      logging.info(' now: %s', _fdatetime(now))
      logging.info(' expires: %s', _fdatetime(radarUrl.expires))
    else:
      self.updateRadarUrl(radarUrl, now)
  
    return radarUrl
        
  def queryRadarUrl(self):
    radarUrl = RadarUrl.get_by_key_name('url')
    if radarUrl:
      logging.info('url from db')
      memcache.Client().set('RadarUrl', radarUrl)
      return radarUrl


  def updateRadarUrl(self, radarUrl, now):
    response = urlfetch.fetch(_RADAR_BASE_URL)

    if response.status_code != 200:
      logging.error('cannot get url: %i, %s', response.status_code, response.status_text)
      return

    doc = BeautifulSoup(response.content, convertEntities=BeautifulSoup.ALL_ENTITIES)
    a = doc.findAll('a', href=re.compile('.*radar_deutschland.*'))
      
    if not a: 
      logging.error('cannot find url in response')
      return

    url = urlparse.urljoin(_RADAR_BASE_URL, a[0]['href'])
    date = _pdatetime(response.headers['Date'])
    delta = date - now

    radarUrl.last_modified = _pdatetime(response.headers['Last-Modified']) - delta

    logging.info('url reloaded')
    logging.info(' now: %s', _fdatetime(now))
    logging.info(' date: %s', _fdatetime(date))
    logging.info(' delta: %f', _seconds(delta))
    logging.info(' last modified: %s', _fdatetime(radarUrl.last_modified))

    if url == radarUrl.url:
      logging.info(' url not updated')
      radarUrl.expires = now + timedelta(seconds=5)
      if radarUrl.expires > radarUrl.last_modified + timedelta(seconds=radarUrl.max_age):
        radarUrl.max_age = (radarUrl.max_age*9 + _seconds(radarUrl.expires-radarUrl.last_modified)) / 10
        logging.info('   max-age: %f', radarUrl.max_age)
    else:
      logging.info(' url updated')

      if radarUrl.last_modified < radarUrl.expires:
        radarUrl.max_age = radarUrl.max_age - (_seconds(radarUrl.expires-radarUrl.last_modified)) / 10
        logging.info('   max-age: %f', radarUrl.max_age)
        
      radarUrl.url = url;
      radarUrl.expires = radarUrl.last_modified + timedelta(seconds=radarUrl.max_age)

      self.saveTimestamp(radarUrl)

    logging.info(' expires: %s', _fdatetime(radarUrl.expires))

    db.put(radarUrl)
    memcache.Client().set('RadarUrl', radarUrl)

  def saveTimestamp(self, radarUrl):
    parts = radarUrl.url.rpartition('/')[-1].split('_')
    time = datetime.strptime(parts[2][:4] + '-' + parts[2][4:6] + '-' + parts[2][6:8] + ' ' + 
                      parts[3][:2] + ':' + parts[3][2:] + ' GMT', '%Y-%m-%d %H:%M %Z')
    last_modified = radarUrl.last_modified
    delta = _seconds(last_modified-time)
    RadarTimestamp(time=time.strftime('%H%M'), delta=delta, last_modified=last_modified).put()

class RadarOverlaysHandler(RadarHandler):
  def get(self):
    radarUrl = self.getRadarUrl()

    if radarUrl.url:
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.headers['Last-Modified'] = _fdatetime(radarUrl.last_modified)
      self.response.headers['Expires'] = _fdatetime(radarUrl.expires)
      self.response.headers['Cache-Control'] = 'max-age=%i' % _seconds(radarUrl.expires-datetime.utcnow())
      self.response.out.write('// ')

      overlays = self.getRadarOverlaysJS(radarUrl)

      if overlays:
        self.response.out.write(overlays)
      else:
        self.response.out.write('[]')
        
    else:
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.out.write('// []')



class RadarUrlHandler(RadarHandler):
  def get(self):
    radarUrl = self.getRadarUrl()

    if radarUrl.url:
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.headers['Last-Modified'] = _fdatetime(radarUrl.last_modified)
      self.response.headers['Expires'] = _fdatetime(radarUrl.expires)
      self.response.headers['Cache-Control'] = 'max-age=%i' % _seconds(radarUrl.expires-datetime.utcnow())
      self.response.out.write(radarUrl.url)
    else:
      self.response.headers['Content-Type'] = 'text/plain'
      self.response.out.write('no radarUrl')


class RadarMapHandler(RadarHandler):
  def get(self):
    template_values = { }
    self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
    self.response.out.write(template.render(_MAP_TEMPLATE, template_values))
    





def _pdatetime(s):
  return datetime.strptime(s, '%a, %d %b %Y %H:%M:%S %Z')

def _fdatetime(t):
  return t.strftime('%a, %d %b %Y %H:%M:%S GMT')

def _seconds(d):
  return d.days * 60*60*24 + d.seconds + d.microseconds / (1000*1000.0)


def main():
  application = webapp.WSGIApplication(
    [('/wetter/radarUrl', RadarUrlHandler),
     ('/wetter/radarOverlays', RadarOverlaysHandler),
     ('/wetter', RadarMapHandler)
    ],
    debug=True)
  wsgiref.handlers.CGIHandler().run(application)

if __name__ == "__main__":
  main()
